package kit.token;

import java.io.IOException;
import java.security.Key;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;
import org.jose4j.jwe.JsonWebEncryption;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.keys.AesKey;
import org.jose4j.lang.JoseException;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.jfinal.plugin.ehcache.CacheKit;

import exception.NoTokenFoundException;

public class TokenValidator {
	
	private static Logger LOGGER = Logger.getLogger( TokenValidator.class );


    /**
	 * Utility Method to create a Consumer if it doesn't exists, otherwise,
	 * return existing one.
	 * 
	 * @throws NoTokenFoundException
	 */
	private static JwtConsumer getConsumer(String appkey) throws NoTokenFoundException {
		TokenConfiguration tokenconfig = CacheKit.get("oauth", appkey);
		if (tokenconfig != null) {
			return tokenconfig.getConsumer();
		} else {
			throw new NoTokenFoundException("can't find token");
		}
	}

    /**
	 * Validate and return custom claims passed.
	 * 
	 * @param token
	 *            to validate.
	 *
	 * @return Map of custom claims
	 * @throws NoTokenFoundException
	 */
	public static Map<String, String> validate(String appkey, String token)
			throws InvalidJwtException, JoseException, NoTokenFoundException {


		JwtClaims claims = getConsumer(appkey).processToClaims(token);
		return decrypt(appkey, (String) claims.getClaimValue("identity"));

	}
    
    /**
	 * Validate and return custom claims passed.
	 * 
	 * @param type
	 *            Class in whichi payload needs to be stored.
	 * @param token
	 *            token to validate.
	 *
	 * @return Instance of type.
	 * @throws IOException
	 * @throws JsonMappingException
	 * @throws JsonParseException
	 * @throws InvalidJwtException
	 * @throws NoTokenFoundException
	 */
    @SuppressWarnings({ "unchecked", "rawtypes" })
	public static Object validate(String appkey, Class holder, String token)
			throws JoseException, IOException, InvalidJwtException, NoTokenFoundException {
		JwtClaims jClaims = getConsumer(appkey).processToClaims(token);
		Map<String, String> claims = decrypt(appkey, (String) jClaims.getClaimValue("identity"));
        
		return JSON.parseObject(JSON.toJSONString(claims), holder);
    }
    
    /**
	 * We store custom payload in identity claim in encrypted format. Here, we
	 * decrypt the payload and return obtained map.
	 * 
	 * @throws NoTokenFoundException
	 */
	private static Map<String, String> decrypt(String appkey, String identity)
			throws JoseException, NoTokenFoundException {
		TokenConfiguration tokenconfig = CacheKit.get("oauth", appkey);
		if (tokenconfig == null) {
			throw new NoTokenFoundException("can't find token");
		}
    	Map<String, String> claims = null;
        
		String aesKey = tokenconfig.getEncryptionKey();
        Key key = new AesKey( aesKey.getBytes() );
        
        JsonWebEncryption encryption = new JsonWebEncryption();
        encryption.setKey( key );
        encryption.setCompactSerialization( identity );
        
        String payLoad = encryption.getPayload();
        
		claims = JSON.parseObject(payLoad, new TypeReference<HashMap<String, String>>() {
		});
        return claims;
        
    }
}
